package com.pug.zixun.controller.login;

import com.pug.zixun.bo.PugUserBo;
import com.pug.zixun.commons.enums.AdminUserResultEnum;
import com.pug.zixun.commons.ex.PugValidatorException;
import com.pug.zixun.commons.utils.fn.asserts.Vsserts;
import com.pug.zixun.commons.utils.pwd.DesUtils;
import com.pug.zixun.commons.utils.pwd.MD5Util;
import com.pug.zixun.config.redis.AdminRedisKeyManager;
import com.pug.zixun.config.redis.IJwtBlackService;
import com.pug.zixun.controller.BaseController;
import com.pug.zixun.pojo.AdminUser;
import com.pug.zixun.service.adminuser.IAdminUserService;
import com.pug.zixun.service.jwt.JwtService;
import com.pug.zixun.vo.LoginVo;
import lombok.extern.slf4j.Slf4j;
import org.pug.generator.anno.PugDoc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.UUID;

/**
 * @author 飞哥
 * @Title: 学相伴出品
 * @Description: 飞哥B站地址：https://space.bilibili.com/490711252
 * 记得关注和三连哦！
 * @Description: 我们有一个学习网站：https://www.kuangstudy.com
 * @date 2022/5/13$ 21:30$
 */
@RestController
@Slf4j
@PugDoc(name = "登录管理", tabname = "kss_user")
public class PassportLoginController extends BaseController implements AdminRedisKeyManager {


    @Autowired
    private IAdminUserService adminUserService;
    @Autowired
    private JwtService jwtService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    @Qualifier("jwtBlackStringService")
    private IJwtBlackService jwtBlackService;

    /**
     * 登录
     *
     * @param loginVo
     * @return
     */
    @PostMapping("/login/toLogin")
    @PugDoc(name = "登录管理")
    public PugUserBo logined(@RequestBody LoginVo loginVo) {
        // 这里有校验，spring-validator框架来完成 或者用断言 或者用自己封装的
        Vsserts.isEmptyEx(loginVo.getUsername(), AdminUserResultEnum.USER_NAME_NOT_EMPTY);
        Vsserts.isEmptyEx(loginVo.getPassword(), AdminUserResultEnum.USER_PWD_NOT_EMPTY);
        Vsserts.isEmptyEx(loginVo.getCode(), AdminUserResultEnum.USER_CODE_NOT_EMPTY);
        // 根据uuid获取redis缓存中的验证码信息
        String redisCode = stringRedisTemplate.opsForValue().get(loginVo.getCodeUuid());
        Vsserts.isEmptyEx(redisCode, AdminUserResultEnum.USER_CODE_NOT_EMPTY);
        // 把用户输入的code和缓存的redisCode
        if (! redisCode.equalsIgnoreCase(loginVo.getCode())){
            throw new PugValidatorException(AdminUserResultEnum.USER_CODE_INPUT_ERROR);
        }
        // 记得把用户名解密
        loginVo.setUsername(DesUtils.decrypt(loginVo.getUsername()));
        // 根据用户名称查询用户信息
        AdminUser dbLoginUser = adminUserService.login(loginVo);
        Vsserts.isNullEx(dbLoginUser, AdminUserResultEnum.USER_NULL_ERROR);
        Vsserts.isTrueEx(dbLoginUser.getStatus().equals(0), AdminUserResultEnum.USER_FORBIDDEN_ERROR);
        Vsserts.isTrueEx(dbLoginUser.getIsdelete().equals(1), AdminUserResultEnum.USER_FORBIDDEN_ERROR);

        // 用户输入的密码
        String inputPwd = DesUtils.decrypt(loginVo.getPassword());
        String inputMd5Pwd = MD5Util.md5slat(inputPwd);

        // 如果输入密码和数据库密码不一致
        boolean isLogin = dbLoginUser.getPassword().equalsIgnoreCase(inputMd5Pwd);
        // 如果输入的账号和有误，isLogin=false.注意isFalseEx在里面取反的，所以会抛出异常
        Vsserts.isFalseEx(isLogin, AdminUserResultEnum.USER_INPUT_USERNAME_ERROR);

        PugUserBo userBo = new PugUserBo();
        // 根据用户生成token
        String token = jwtService.createToken(dbLoginUser.getId());
        userBo.setToken(token);
        // 注意把一些敏感信息全部清空返回
        dbLoginUser.setPassword(null);
        userBo.setUser(dbLoginUser);
        userBo.setRoleNames(adminUserService.getRoleNames(dbLoginUser.getId()));
        userBo.setPermissions(adminUserService.findByUserPermission(dbLoginUser.getId()));
        // 登录挤下线
        String tokenUuid = UUID.randomUUID().toString();
        String tokenUuidKey = USER_LOGIN_LOGOUT_KEY + dbLoginUser.getId();
        stringRedisTemplate.opsForValue().set(tokenUuidKey, tokenUuid);
        userBo.setTokenUuid(tokenUuid);
        // 登录创建双倍时间，用于续期
        jwtService.redisToken(token);

        return userBo;
    }


    /**
     * 退出登录
     * @param request
     * @return
     */
    @PostMapping("/login/logout")
    @PugDoc(name = "退出登录")
    public String logout(HttpServletRequest request) {
        // 通过请求头获取
        String token = jwtService.getToken(request);
        String userId = jwtService.getTokenUserId(request);
        Vsserts.isEmptyEx(token, AdminUserResultEnum.TOKEN_NOT_FOUND);
        Vsserts.isNullEx(userId, AdminUserResultEnum.USER_NAME_NOT_EMPTY);
        // 删除下线的uuid
        String tokenUuidKey = USER_LOGIN_LOGOUT_KEY + userId;
        stringRedisTemplate.delete(tokenUuidKey);
        // 删除续期redis的key
        String tokenKey = USER_LOGIN_TOKEN_KEY + token;
        stringRedisTemplate.delete(tokenKey);
        // 加黑名单，防止下次使用，它可能是在刚刚生成的时候就退出了。退出逻辑一定力求不管你token有效还是过期都应该后续不能在使用
        jwtBlackService.addBlackList(token);
        // 退出登录
        return "success";
    }

}
